/*
 * RegionEditorPanel.java
 * Created on Aug 23, 2010, 7:24:06 AM
 */

package equidistribution.gui;

import equidistribution.scenario.EquiController;
import java.awt.Color;
import java.awt.Image;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JOptionPane;
import javax.swing.table.AbstractTableModel;
import primitive.style.ShapeStyle;
import visometry.plane.PlaneAxes;
import visometry.plottable.VImage;
import visometry.plottable.VShape;

/**
 *
 * @author elisha
 */
public class RegionEditorPanel extends javax.swing.JPanel {

    ZoneTableModel ztm;

    VImage<Point2D.Double> bgImage;
    EquiFileHandler efh;
    VShape<Point2D.Double> regionShape;
    ArrayList<VShape<Point2D.Double>> zoneShapes = new ArrayList<VShape<Point2D.Double>>();
    ArrayList<Double> zonePriorities = new ArrayList<Double>();
    ShapeStyle regionStyle = new ShapeStyle(new Color(64, 128, 32), new Color(192, 255, 128));
    ShapeStyle zoneStyle = new ShapeStyle(new Color(192, 32, 32), new Color(255, 255, 128));


    /** Constructs editor with a default region. */
    public RegionEditorPanel() {
        initComponents();
        zoneTable.setModel(ztm = new ZoneTableModel(zonePriorities));
        plot.add(new PlaneAxes("x", "y", PlaneAxes.AxesType.BOX));

        initRegion(1.0, new Point2D.Double(0,0), new Point2D.Double(1,0), new Point2D.Double(1,1), new Point2D.Double(0,1));
        zoneSp.setValue(2); // this will automatically add the zones
    }

    /** Constructs region editor with scenario from specified controller. */
    public RegionEditorPanel(EquiController sc) {
        initComponents();
        zoneTable.setModel(ztm = new ZoneTableModel(zonePriorities));
        plot.add(new PlaneAxes("x", "y", PlaneAxes.AxesType.BOX));

        initRegion(sc.getPriority(), sc.getBorder());
        Point2D.Double[][] zones = sc.getZones();
        Double[] pZones = sc.getZonePriority();
        if (zones.length != pZones.length) {
            System.out.println("WARNING -- zone array of different length than priority array!");
            System.out.println("zones: " + Arrays.deepToString(zones));
            System.out.println("pzones: " + Arrays.deepToString(pZones));
        }
        for (int i = 0; i < Math.min(zones.length, pZones.length); i++)
            addNewZone(pZones[i], zones[i]);
        zoneSp.setValue(Math.min(zones.length, pZones.length));
    }

    private void initRegion(double priority, Point2D.Double... points) {
        regionShape = new VShape<Point2D.Double>(points);
        regionStyle.setThickness(2f);
        regionShape.setShapeStyle(regionStyle);
        plot.add(regionShape);
        fitBActionPerformed(null);

        prioritySp.setValue(priority);
    }

    /** Copy setup to specified controller. */
    public void copyRegionTo(EquiController sc) {
        sc.setAdjusting(true);
        sc.setBorder(regionShape.getPoint());
        sc.setPriority((Double) prioritySp.getValue());
        ArrayList<Point2D.Double[]> zones = new ArrayList<Point2D.Double[]>();
        for (VShape<Point2D.Double> zsh : zoneShapes)
            zones.add(zsh.getPoint());
        sc.setZones(zones.toArray(new Point2D.Double[][]{}));
        sc.setZonePriority(zonePriorities.toArray(new Double[]{}));
        sc.randomizeAgentLocations();
        sc.setAdjusting(false);
    }

    private void addNewZone() {
        addNewZone(1.0, new Point2D.Double(Math.random(), Math.random()),
                new Point2D.Double(Math.random(), Math.random()),
                new Point2D.Double(Math.random(), Math.random()) );
    }

    private void addNewZone(double priority, Point2D.Double... points) {
        VShape<Point2D.Double> zone = new VShape<Point2D.Double>(points);
        zone.setShapeStyle(zoneStyle);
        zoneShapes.add(zone);
        plot.add(zone);
        zonePriorities.add(priority);
    }

    private void removeZone() {
        int n = zoneShapes.size();
        VShape<Point2D.Double> zone = zoneShapes.remove(n-1);
        if (plot.getMouseInputListener() == zone)
            plot.setMouseInputListener(null);
        plot.remove(zone);
        zonePriorities.remove(n-1);
    }

    public int getNumberOfZones() {
        return (Integer) zoneSp.getValue();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        toolbar = new javax.swing.JToolBar();
        imageLoadB = new javax.swing.JButton();
        imageCloseB = new javax.swing.JButton();
        jSeparator1 = new javax.swing.JToolBar.Separator();
        editB = new javax.swing.JToggleButton();
        jSeparator2 = new javax.swing.JToolBar.Separator();
        fitB = new javax.swing.JButton();
        jPanel1 = new javax.swing.JPanel();
        jLabel4 = new javax.swing.JLabel();
        zoneSp = new javax.swing.JSpinner();
        jLabel3 = new javax.swing.JLabel();
        prioritySp = new javax.swing.JSpinner();
        jLabel5 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        zoneTable = new javax.swing.JTable();
        plot = new visometry.plane.PlanePlotComponent();

        setLayout(new java.awt.BorderLayout());

        toolbar.setFloatable(false);
        toolbar.setRollover(true);

        imageLoadB.setText("Load Background Image");
        imageLoadB.setFocusable(false);
        imageLoadB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        imageLoadB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        imageLoadB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                imageLoadBActionPerformed(evt);
            }
        });
        toolbar.add(imageLoadB);

        imageCloseB.setText("Close Background Image");
        imageCloseB.setFocusable(false);
        imageCloseB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        imageCloseB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        imageCloseB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                imageCloseBActionPerformed(evt);
            }
        });
        toolbar.add(imageCloseB);
        toolbar.add(jSeparator1);

        editB.setText("Draw Region or Zone");
        editB.setToolTipText("<html>\nClick this to be able to edit a particular region.<br>\nYou will be able to select whether you want to<br>\nedit the entire region or one of the zones.\n<br><br>\nWhen editing, click to select the points in the shape.<br>\n<b>WARNING:</b> This will erase the previous shape!\n<br><br>\nClick this button again to stop editing.");
        editB.setFocusable(false);
        editB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        editB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        editB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                editBActionPerformed(evt);
            }
        });
        toolbar.add(editB);
        toolbar.add(jSeparator2);

        fitB.setText("Fit Region to Window");
        fitB.setToolTipText("This will fit the window around the currently drawn region.");
        fitB.setFocusable(false);
        fitB.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        fitB.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        fitB.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fitBActionPerformed(evt);
            }
        });
        toolbar.add(fitB);

        add(toolbar, java.awt.BorderLayout.PAGE_START);

        jPanel1.setPreferredSize(new java.awt.Dimension(200, 500));
        jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.PAGE_AXIS));

        jLabel4.setText("# Zones:");
        jPanel1.add(jLabel4);

        zoneSp.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(1), Integer.valueOf(0), null, Integer.valueOf(1)));
        zoneSp.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                zoneSpStateChanged(evt);
            }
        });
        jPanel1.add(zoneSp);

        jLabel3.setText("Main Region Priority:");
        jPanel1.add(jLabel3);

        prioritySp.setModel(new javax.swing.SpinnerNumberModel(Double.valueOf(0.0d), Double.valueOf(0.0d), null, Double.valueOf(0.5d)));
        jPanel1.add(prioritySp);

        jLabel5.setText("Zone Priorities:");
        jPanel1.add(jLabel5);

        zoneTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {new Integer(0), new Float(1.0)}
            },
            new String [] {
                "Zone #", "Priority"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Integer.class, java.lang.Float.class
            };
            boolean[] canEdit = new boolean [] {
                false, true
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        jScrollPane1.setViewportView(zoneTable);

        jPanel1.add(jScrollPane1);

        add(jPanel1, java.awt.BorderLayout.WEST);

        plot.setBackground(new java.awt.Color(255, 252, 245));
        plot.setForeground(new java.awt.Color(255, 255, 255));
        add(plot, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void imageLoadBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_imageLoadBActionPerformed
        if (efh == null)
            efh = new EquiFileHandler();
        Image img = efh.loadImage();
        if (img == null) {
            imageCloseBActionPerformed(evt);
        } else {
            double width = img.getWidth(null);
            double height = img.getHeight(null);
            if (bgImage == null) {
                bgImage = new VImage<Point2D.Double>(new Point2D.Double(0,1), img);
                bgImage.setCorner(new Point2D.Double(2.0, 1-2.0*height/width));
                plot.add(0, bgImage);
            } else {
                bgImage.setImage(img);
                Point2D.Double anchor = bgImage.getAnchor();
                bgImage.setCorner(new Point2D.Double(anchor.x + 2.0, anchor.y - 2.0*height/width));
            }
        }
}//GEN-LAST:event_imageLoadBActionPerformed

    private void imageCloseBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_imageCloseBActionPerformed
        if (bgImage != null) {
            plot.remove(bgImage);
            bgImage = null;
        }
}//GEN-LAST:event_imageCloseBActionPerformed

    private void fitBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fitBActionPerformed
        double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE,
                maxX = -Double.MAX_VALUE, maxY = -Double.MAX_VALUE;
        for (Point2D.Double p : regionShape.getPoint()) {
            minX = Math.min(minX, p.x);
            minY = Math.min(minY, p.y);
            maxX = Math.max(maxX, p.x);
            maxY = Math.max(maxY, p.y);
        }
        double rangeX = minX == maxX ? 1 : maxX - minX;
        double rangeY = minY == maxY ? 1 : maxY - minY;
        plot.setDesiredRange(minX - rangeX/10., minY - rangeY/10., maxX + rangeX/10., maxY + rangeY/10.);
}//GEN-LAST:event_fitBActionPerformed

    private void zoneSpStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_zoneSpStateChanged
        int n = getNumberOfZones();
        if (n != zoneShapes.size()) {
            while (n > zoneShapes.size())
                addNewZone();
            while (n < zoneShapes.size())
                removeZone();
            ztm.fireTableDataChanged();
        }
}//GEN-LAST:event_zoneSpStateChanged

    transient VShape<Point2D.Double> editShape = null;

    private void editBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editBActionPerformed
        if (editB.isSelected()) {
            String[] values = new String[getNumberOfZones()+1];
            String regionValue = values[0] = "Region";
            for (int i = 0; i < getNumberOfZones(); i++)
                values[i+1] = Integer.toString(i);

            Object result = JOptionPane.showInputDialog(this, "Select zone to edit.","Select region/zone", JOptionPane.QUESTION_MESSAGE, null, values, regionValue);

            if (result == null)
                editShape = null;
            else if (result == regionValue)
                editShape = regionShape;
            else {
                for (int i = 1; i < values.length; i++)
                    if (result == values[i]) {
                        editShape = zoneShapes.get(i-1);
                        break;
                    }
            }

            if (plot.getMouseInputListener() != null)
                ((VShape<Point2D.Double>)plot.getMouseInputListener()).setPointsVisible(false);

            if (editShape != null) {
                editShape.setPoint(new Point2D.Double[]{});
                editShape.setPointsVisible(true);
            }
            plot.setMouseInputListener(editShape);
        } else {
            editShape.setPointsVisible(false);
        }
    }//GEN-LAST:event_editBActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JToggleButton editB;
    private javax.swing.JButton fitB;
    private javax.swing.JButton imageCloseB;
    private javax.swing.JButton imageLoadB;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JToolBar.Separator jSeparator1;
    private javax.swing.JToolBar.Separator jSeparator2;
    private visometry.plane.PlanePlotComponent plot;
    private javax.swing.JSpinner prioritySp;
    private javax.swing.JToolBar toolbar;
    private javax.swing.JSpinner zoneSp;
    private javax.swing.JTable zoneTable;
    // End of variables declaration//GEN-END:variables

}
